﻿using System;
using System.Net.Sockets;
using System.Threading;
using CommonTypes.Server;
using CommonTypes.Server.Operations;
using Server.Model;
using Server.Services.Primary;
using Server.Services.Secondary;

namespace Server.Services.RemoteObjects
{
    public class ServerToServer : MarshalByRefObject, IServerToServer
    {
        private readonly ServerData _servData;

        public ServerToServer( ServerData _servData )
        {
            this._servData = _servData;
        }

        public ImAliveResponse ImAlive( String primaryId, long logicalClock )
        {
            if( !_servData.Standalone )
            {
                System.Console.WriteLine( "I'm processing a ping" );
                switch( _servData.State )
                {
                    case ServerState.ESTABLISHED:
                        return SecondaryPingManager.processPing( primaryId, logicalClock, _servData );
                    case ServerState.ADDREMOVE:
                        return ImAliveResponse.OK;
                    case ServerState.BULLYING:
                        return ImAliveResponse.PRIMARY_UNKNOWN;
                    default:
                        return ImAliveResponse.ERROR;
                }
            }
            else
                return ImAliveResponse.OK;
        }

        public void GenerateTicket( String clientId, DateTime clientNonce )
        {
            if( _servData.Standalone )
                normalGenerateTicket( clientId, clientNonce );
            else
            {
                if( _servData.State == ServerState.ESTABLISHED )
                    normalGenerateTicket( clientId, clientNonce );
                else
                {
                    holdRequests( );
                    normalGenerateTicket( clientId, clientNonce );
                }
            }
        }

        private void holdRequests( )
        {
            while( _servData.State != ServerState.ESTABLISHED )
                Thread.Sleep( 200 );
        }

        private void normalGenerateTicket( String clientId, DateTime clientNonce )
        {
            if( clientId == null || clientId.Length == 0 || !_servData.UsersList.ContainsKey( clientId ) )
                return;

            ProcessingRequests.GenerateTicket( clientId, clientNonce, _servData );
        }

        public bool AcknowledgeTicket( long ticketId, String clientId, long logicalClock )
        {
            if( _servData.State == ServerState.ESTABLISHED )
            {
                if( _servData.ServersInformation.Primary )
                    return true;
                System.Console.WriteLine( "I received a ticket acknowledge!" );

                SecondaryMergingManager.AcknowledgeTicket( ticketId, logicalClock, _servData );
            }
            return true;
        }

        public bool RegisterClient( String clientId, String clientLocation, long logicalClock )
        {
            if( _servData.State == ServerState.ESTABLISHED )
            {
                if( _servData.ServersInformation.Primary )
                    return ProcessingRequests.RegisterClient( clientId, clientLocation, _servData );
                System.Console.WriteLine( "I received a client registration!" );

                SecondaryMergingManager.RegisterClient( clientId, clientLocation, logicalClock, _servData );
            }
            return true;
        }

        public bool UnregisterClient( String clientId, long logicalClock )
        {
            if( _servData.State == ServerState.ESTABLISHED )
            {
                if( _servData.ServersInformation.Primary )
                    return ProcessingRequests.UnregisterClient( clientId, _servData );
                System.Console.WriteLine( "I received a client unregistration!" );

                SecondaryMergingManager.UnregisterClient( clientId, logicalClock, _servData );
            }
            return true;
        }

        BullyResponse IServerToServer.ElectPrimary( string serverId )
        {
            return BullyProtocol.electPrimary( serverId, _servData );
        }

        public override object InitializeLifetimeService( )
        {
            return null;
        }

        //The reason this method throws SocketException if it is being added, is to prevent a deadlock to
        //happen if other server asks this server who is the current primary at the same time, for example
        //when adding two servers simultaneously...
        public string currentPrimaryId( )
        {
            if( _servData.State == ServerState.ESTABLISHED || _servData.State == ServerState.BULLYING )
                return _servData.ServersInformation.PrimaryId;
            else
                throw new SocketException( );
        }

        public void AcknowledgePrimary( string serverId )
        {
            BullyProtocol.AcknowledgePrimary( serverId, _servData );
        }

        public Operation[ ] SynchronizeServer( long logicalClock )
        {
            return PrimaryMergingManager.SynchronizeServer( logicalClock, _servData );
        }

        public TicketGeneration sendCurrentTicket( )
        {
            return new TicketGeneration( _servData.ServerLogicalClock, _servData.TicketNumber );
        }

        public void registerServer( String serverId )
        {
            _servData.ServersInformation.ServersProxy[ serverId ] = ( IServerToServer )Activator.GetObject( typeof( IServerToServer ), _servData.ServersInformation.ServersLocation[ serverId ] + "/ServerToServer" );
        }
    }
}